package io.mft.commons.ioc;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import cn.hutool.core.util.ObjectUtil;
import io.mft.commons.exception.MFTException;

abstract public class BaseRegistry extends ClassPathScanningCandidateComponentProvider implements Registry{
	protected String basePackage;
	protected String sysNo;
	protected HashMap<String, ApiBeanName> apiIdCache;
	private Class<? extends Annotation> annotationType;
	public BaseRegistry(String sysNo,String basePackage,Class<? extends Annotation> annotationType) {
		super(false);
		addIncludeFilter(new AnnotationTypeFilter(annotationType));
		this.basePackage=basePackage;
		this.sysNo=sysNo;
		apiIdCache=MFTSpringRegistry.getApiIdCache();
		this.annotationType=annotationType;
	}
	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return beanDefinition.getMetadata().isConcrete();
	}
	protected  List<ApiBeanName> parseBeanName(String annotationName,String sysNo,BeanDefinition definition){
		List<ApiBeanName> list=new ArrayList<ApiBeanName>();
		AnnotatedBeanDefinition annotatedBeanDefinition=(AnnotatedBeanDefinition)definition;
		Map<String, Object> map=annotatedBeanDefinition.getMetadata().getAnnotationAttributes(annotationName);
		Object value=map.get("value");
		if (value!=null&&!ObjectUtil.isEmpty(String.valueOf(value))) {
			String[] apis=annotatedBeanDefinition.getMetadata().getInterfaceNames();
			for (String api : apis) {
				String[] api_arr=api.split("\\.");
				String beanName =sysNo+"."+String.valueOf(value)+"."+api_arr[api_arr.length-1];
				ApiBeanName api_beanName=new ApiBeanName();
				api_beanName.setBeanName(beanName);
				api_beanName.setQualifiedName(api);
				api_beanName.setBeanClassName(definition.getBeanClassName());
				api_beanName.setApiInterface(true);
				api_beanName.setAnnotationType(annotationName);
				api_beanName.setSysNo(sysNo);
				list.add(api_beanName);
			}
		}else {
			String[] apis=annotatedBeanDefinition.getMetadata().getInterfaceNames();
			for (String api : apis) {
				String[] api_arr=api.split("\\.");
				String beanName =sysNo+"."+api_arr[api_arr.length-1];
				ApiBeanName api_beanName=new ApiBeanName();
				api_beanName.setBeanName(beanName);
				api_beanName.setBeanClassName(definition.getBeanClassName());
				api_beanName.setQualifiedName(api);
				api_beanName.setApiInterface(true);
				api_beanName.setAnnotationType(annotationName);
				api_beanName.setSysNo(sysNo);
				list.add(api_beanName);
			}
		}
		return list;
	}
    protected boolean handle(AbstractBeanDefinition genericBeanDefinition) throws Exception {
    	return true;
    }
    protected void handle(ApiBeanName apiBeanName) throws Exception {
    }
	protected List<ApiBeanName> load(BeanDefinitionRegistry registry){
		try {
			List<ApiBeanName> list=new ArrayList<ApiBeanName>();
			Set<BeanDefinition> beanDefinitions=findCandidateComponents(basePackage);
			for (BeanDefinition definition : beanDefinitions) {
				List<ApiBeanName> beanNames=parseBeanName(annotationType.getName(),sysNo,definition);
				for (ApiBeanName apiBeanName : beanNames) {
					handle(apiBeanName);
					RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Thread.currentThread().getContextClassLoader().loadClass(definition.getBeanClassName()));
					rootBeanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
					boolean handle=handle(rootBeanDefinition);
					if (handle) {
						if (apiIdCache.containsKey(apiBeanName.getBeanName())) {
							String t_beanClassName=apiIdCache.get(apiBeanName.getBeanName()).getBeanClassName();
							throw new MFTException("子系统："+sysNo+">>>存在相同的api接口[beanId:"+apiBeanName.getBeanName()+"],当前接口["+definition.getBeanClassName()+"]>>>[接口<"+apiBeanName.getQualifiedName()+">在其它实例<"+t_beanClassName+">[注解类型<"+apiBeanName.getAnnotationType()+">]已被spring托管;相同接口的实现不能同时被标记]请检查!");
						}
						registry.registerBeanDefinition(apiBeanName.getBeanName(), rootBeanDefinition);
					    apiBeanName.setBeanClassName(definition.getBeanClassName());
					    apiIdCache.put(apiBeanName.getBeanName(), apiBeanName);
					    list.add(apiBeanName);
						logger.warn("子系统："+sysNo+"-->注册"+annotationType.getSimpleName()+"-api:[beanId:"+apiBeanName.getBeanName()+"]"+definition.getBeanClassName());
					}
				}
			}
			return list;
		} catch (Exception e) {
			logger.error("", e);
			return null;
		}
	}
	
	@Override
	public void reg(BeanDefinitionRegistry registry) {
		load(registry);
	}
}
